home *** CD-ROM | disk | FTP | other *** search
-
- package sub_arctic.lib;
-
-
- import sub_arctic.input.text_acceptor;
- import sub_arctic.input.pressable;
- import sub_arctic.input.simple_draggable;
- import sub_arctic.input.event;
-
- import java.awt.Font;
- import java.awt.Point;
-
- /**
- * Multi-line (but single font) text edit area. This class allows simple
- * entry and editing of multiple lines of text.
- *
- * @author Scott Hudson
- */
- public class text_edit extends text_display
- implements text_acceptor, pressable, simple_draggable {
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Full constructor with explicit size. Text contents is provided by
- * one string with lines separated by newlines or carriage returns.
- *
- * @param int xv the x position of the object.
- * @param int yv the y position of the object.
- * @param int wv the width of the object.
- * @param int hv the height of the object.
- * @param String contents the string to put into the editing area.
- * @param boolean boxed true if this object should have a box around it.
- */
- public text_edit(
- int xv, int yv, int wv, int hv,
- String contents,
- Font fnt,
- boolean boxed)
- {
- super(xv,yv,wv,hv, contents, fnt, boxed);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Full constructor with size determined by initial text. Text contents
- * is provided by one string with lines separated by newlines or carriage
- * returns.
- *
- * @param int xv the x position of the object.
- * @param int yv the y position of the object.
- * @param String contents the string to put into the editing area.
- * @param boolean boxed true if this object should have a box around it.
- */
- public text_edit(
- int xv, int yv,
- String contents,
- Font fnt,
- boolean boxed)
- {
- super(xv,yv, contents, fnt, boxed);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Constructor with computed size, default font, and default
- * boxed status.
- *
- * @param int xv the x position of the object.
- * @param int yv the y position of the object.
- * @param String contents the string to put into the editing area.
- */
- public text_edit(int xv, int yv, String contents)
- {
- super(xv,yv,contents);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Replace the area of the current selection with the given string. The
- * selection is set to a single point after the new text. Since the
- * the replacement string can be empty, this can be used to delete the
- * selection.
- *
- * @param String with_str the new string to put where the selection is now.
- */
- public void replace_selection(String with_str)
- {
- String line, before, after, rest;
- int nl, nl1, nl2, insert_loc, insert_pos, str_pos;
-
- /* if they pass a null treat that as an empty string */
- if (with_str == null) with_str = "";
-
- /* if there is no selection, bail out early */
- if (selection_start_line() == NO_SELECTION) return;
-
- /* extract the pieces before and after the selection */
- line = (String)text().elementAt(selection_start_line());
- before = line.substring(0,selection_start_pos());
- line = (String)text().elementAt(selection_end_line());
- after = line.substring(selection_end_pos());
-
- /* remove all the lines that include selection parts */
- for (int cnt=0; cnt<selection_end_line()-selection_start_line()+1; cnt++)
- {
- text().removeElementAt(selection_start_line());
- }
-
- /* add in new string breaking up into lines as we go */
- line = before;
- insert_loc = selection_start_line();
- insert_pos = selection_start_pos();
- for (str_pos = 0; ; str_pos = nl+1)
- {
- /* find the first line break after the current pos in new string */
- nl1 = with_str.indexOf('\n', str_pos);
- nl2 = with_str.indexOf('\r', str_pos);
- if (nl1 < nl2 || nl2 == -1)
- nl = nl1;
- else
- nl = nl2;
-
- /* if there is no newline append the rest of the string plus the part
- * after the selection and we are done */
- if (nl == -1)
- {
- rest = with_str.substring(str_pos);
- text().insertElementAt(line + rest + after, insert_loc);
- insert_pos += rest.length();
- break;
- }
- else
- {
- /* otherwise append the piece we have and move to a new line */
- text().insertElementAt(line + with_str.substring(str_pos,nl),
- insert_loc);
- insert_loc++;
- insert_pos = 0;
- line = "";
- }
- }
-
- /* set selection to be after the last inserted text */
- set_selection(insert_loc, insert_pos, insert_loc, insert_pos);
-
- damage_self();
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Delete the character before the current selection (backing the selection
- * up one character). If there is no selection or the selection starts at
- * the beginning of the string, nothing happens.
- */
- public void delete_a_char()
- {
- int prev_line, prev_len;
- String line, before, after;
-
- /* if there is no selection or we are at the start of the text, bail now*/
- if (selection_start_line() == NO_SELECTION ||
- (selection_start_line() == 0 && selection_start_pos() == 0))
- return;
-
- /* if selection is at the start of a line do a join with previous line */
- if (selection_start_pos() == 0)
- {
- /* concat selection line onto prev line */
- prev_line = selection_start_line()-1;
- line = (String)text().elementAt(prev_line);
- prev_len = line.length();
- line = line + (String)text().elementAt(selection_start_line());
-
- /* use new line, get rid of old line */
- text().setElementAt(line, prev_line);
- text().removeElementAt(selection_start_line());
-
- /* Set selection point at end of old text */
- set_selection(prev_line, prev_len, prev_line, prev_len);
-
- damage_self();
- }
- /* otherwise we just kill off a character */
- else
- {
- /* extract piece of line before selection, less on character */
- line = (String)text().elementAt(selection_start_line());
- before = line.substring(0, selection_start_pos()-1);
-
- /* and piece after selection point */
- after = line.substring(selection_start_pos());
-
- /* replace text with concat of that */
- text().setElementAt(before+after, selection_start_line());
-
- /* move selection point back one */
- set_selection(selection_start_line(), selection_start_pos()-1,
- selection_start_line(), selection_start_pos()-1);
-
- damage_self();
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Map an x position in local coordinates into a text string position (index)
- * on a given line.
- *
- * @param int on_ln the line to be checking.
- * @param int x_pos the x coordinate to check (in pixels).
- * @return int the character number
- */
- public int char_pos_of(int on_ln, int x_pos)
- {
- int len, front, mid, back, dist;
- String line;
- char[] ln_text;
-
- /* first take off x margin */
- x_pos -= h_spacing();
-
- /* take care of positions before start */
- if (x_pos < 0) return 0;
-
- /* extract character array from string */
- line = (String)text().elementAt(on_ln);
- len = line.length();
- ln_text = new char[len];
- line.getChars(0,len,ln_text,0);
-
- /* binary search to find the text position before the given point */
- front = 0;
- back = len;
- do {
- mid = (front + back)/2;
- dist = x_pos - _metric.charsWidth(ln_text, 0, mid);
- if (dist > 0)
- front = mid;
- else if (dist == 0)
- return mid;
- else
- back = mid;
- } while (front+1 < back);
-
- /* if we are at the end just take that */
- if (mid == len) return front;
-
- /* now see if we are more than 1/2 way into the final character */
- if (Math.abs(x_pos - _metric.charsWidth(ln_text, 0, front)) >
- Math.abs(x_pos-_metric.charsWidth(ln_text, 0, front+1)))
- return front + 1;
- else
- return front;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Map y position in local coordinates into a line number. If the position
- * is before the first line (index 0) then -1 is returned. If the position
- * is past the last line, then one greater than the index of the last line
- * is returned.
- *
- * @param int y_pos the y position (in pixels) to map to a line number.
- * @return int a line number, -1, or 1+ the last line number.
- */
- public int find_line_of(int y_pos)
- {
- int result;
-
- /* compensate for v_spacing at top */
- y_pos -= v_spacing();
-
- /* find our line */
- result = y_pos / _metric.getHeight();
-
- /* account for first line offset */
- result += first_line();
-
- /* take care of before beginning case */
- if (result < 0) result = -1;
-
- /* take care of after end case */
- if (result > text().size()) result = text().size();
-
- return result;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- /* text_acceptor (input) methods */
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- //xx later we need to track how is the focus and draw focus/non differently
-
- /**
- * Accept input that signifies that object is new text focus and will
- * receive subsequent text input. Returns true if the object accepts the
- * focus.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean start_text_entry(event evt, Object user_info)
- {
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Pre-filter a character before providing it is actually provided as input.
- * This routine gets called to allow the object to modify the character
- * before it is passed to new_char(). Input to the method is the ordinal
- * value of the input character in question along with the modifier mask
- * associated with it. Output should either be the ordinal value of a
- * character or a specific negative value signifying one of several special
- * actions. This routine can be used by subclasses to do a translation
- * (e.g. to all lower case) or to filter out unwanted characters (e.g.
- * everything except decimal digits). Filtering is done by returning the
- * special value DISCARD_CHAR (= -1), which signifies that the character
- * is not to be passed to new_char(). Translation is done by returning
- * the ordinal value of the translated character. In addition, the value
- * CLOSURE_ACTION_CHAR can be returned to indicate that the action_char()
- * method should be invoked instead of new_char(). This is typically done
- * for end of line characters that signify completion of an entry. All
- * modifications to the character are considered local to this object and
- * do not change how the character might be delivered to another object.
- * This routine is not called for cursor movement or other special keys
- * (which are dispatched with special_key()), or characters classified
- * as edit keys (e.g. to delete a character or line). The text input
- * dispatch agent class (text_agent) provides several standard filters that
- * can be called for common operations.
- *
- * @param int input_char the input character to be filtered.
- * @param int modifiers the current state of the modifier keys.
- * @return int the filtered value to enter into the object or a special
- * value (DISCARD_CHAR or CLOSURE_ACTION_CHAR)
- */
- public int char_filter(int input_char, int modifiers)
- {
- /* here we do no filtering and no translation */
- return input_char;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input for a single character. Returns true if the object
- * consumes the character.
- *
- * @param event evt the event to be dispatched.
- * @param char ch the character to be entered.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean new_char(event evt, char ch, Object user_info)
- {
- /* if we don't have any text at all, let's just set the
- text to be this value */
- if ((text()==null) || (text().size()==0)) {
- set_text(String.valueOf(ch));
- return true;
- }
- /* if we have no selection, make one at the end */
- if (selection_start_line() == NO_SELECTION)
- set_selection(SELECT_END,SELECT_END, SELECT_END,SELECT_END);
-
- /* replace selection with the char */
- replace_selection(String.valueOf(ch));
-
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input for a character that has been classified as a closure
- * action (by returning CLOSURE_ACTION_CHAR from char_filter).
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean action_char(event evt, char ch, Object user_info)
- {
- /* Here we consume, but ignore closure characters */
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input for a character signifying a delete. Returns true if the
- * object consumes the input.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean delete_char(event evt, Object user_info)
- {
- /* if we have no selection, we do nothing */
- if (selection_start_line() == NO_SELECTION)
- return true;
-
- /* if we have a point selection delete back from it */
- if (selection_start_line() == selection_end_line() &&
- selection_start_pos() == selection_end_pos())
- delete_a_char();
- /* otherwise replace selection with an empty string */
- else
- replace_selection("");
-
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input for a character signifying a line kill. Here we treat this
- * the same as a delete of the current selection.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean line_kill(event evt, Object user_info)
- {
- /* delete the current selection */
- replace_selection("");
-
- return true;
- // may need to reconsider what this should mean in multi-line editor
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input for a key press signifying a special key (corresponding
- * to KEY_ACTION events). Values passed here are from the key field of the
- * events. These include: DOWN, END, F1, ..., F12, HOME, LEFT,
- * PGDN, PGUP, RIGHT, and UP. Returns true if the object consumes
- * the event.
- *
- * @param event evt the event to be dispatched.
- * @param int key_code the keyboard code of this special key.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean special_key(event evt, int key_code, Object user_info)
- {
- int ln, pos, len;
-
- /* handle LEFT and RIGHT only */
- if (key_code == event.LEFT)
- {
- /* if we have no selection, we are done */
- if (selection_start_line() == NO_SELECTION) return false;
-
- /* if we have a point selection, just move */
- if (selection_start_line() == selection_end_line() &&
- selection_start_pos() == selection_end_pos())
- {
- /* only move if we are not at the beginning of text */
- if (selection_start_line() != 0 || selection_start_pos() != 0)
- {
- /* if we are the beginning of a line */
- if (selection_start_pos() == 0)
- {
- /* move to back of previous line */
- ln = selection_start_line()-1;
- pos = ((String)text().elementAt(ln)).length();
- }
- else
- {
- /* otherwise just back up with the same line */
- ln = selection_start_line();
- pos = selection_start_pos()-1;
- }
- set_selection(ln,pos,ln,pos);
- }
- }
- /* otherwise go to a point selection at the left of current */
- else
- {
- set_selection(selection_start_line(), selection_start_pos(),
- selection_start_line(), selection_start_pos());
- }
-
- return true;
- }
-
- if (key_code == event.RIGHT)
- {
- /* if we have no selection, we are done */
- if (selection_start_line() == NO_SELECTION) return false;
-
- /* if we have a point selection, just move */
- if (selection_start_line() == selection_end_line() &&
- selection_start_pos() == selection_end_pos())
- {
- /* if we are at the end of a line */
- len = ((String)text().elementAt(selection_start_line())).length();
- if (selection_start_pos() > len-1)
- {
- /* move down a line unless we are at the end */
- if (selection_start_line() < text().size()-1)
- {
- ln = selection_start_line()+1;
- pos = 0;
- set_selection(ln,pos,ln,pos);
- }
- }
- else
- {
- /* otherwise just move forward with the same line */
- ln = selection_start_line();
- pos = selection_start_pos()+1;
- set_selection(ln,pos,ln,pos);
- }
- }
- /* otherwise go to a point selection at the right */
- else
- {
- set_selection(selection_end_line(), selection_end_pos(),
- selection_end_line(), selection_end_pos());
- }
-
- return true;
- }
-
- /* we don't want anything else */
- return false;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- //xx later need to track focus and display differently
-
- /**
- * Dispatch input indicating that text input is over (e.g. the text focus
- * has moved elsewhere. Returns true if the object consumes the input.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean end_text_entry(event evt, Object user_info)
- {
- /* here we do nothing */
- return true;
-
- // later we need to go to another highlight style
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- /* pressable (input) methods */
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept mouse button press input to the object. Return true if event
- * was consumed.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to pick when this object was
- * clicked on.
- * @return boolean true if this event was handled.
- */
- public boolean press(event evt, Object user_info)
- {
- /* make us both the text and drag focus */
- manager.text_focus.set_focus_to(this, evt, user_info);
- /* if we don't have any text, there isn't any sense in
- doing a drag */
- if ((text()==null) || (text().size()==0)) {
- return true;
- }
- manager.simple_drag_focus.set_focus_to(this, evt, new Point(0,0));
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Companion method to press() needed to finish pressable. Here we never
- * consume the event.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to pick when this object was
- * clicked on.
- * @return boolean true if this event was handled (it is never handled by
- * this object)
- */
- public boolean release(event evt, Object user_info)
- {
- return false;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- /* simple_draggable (input) methods */
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept the start of a drag to the object.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean drag_start(event evt, Object user_info)
- {
- int start_line, start_pos;
-
- /* establish the start of selection where this event was */
- start_line = find_line_of(evt.local_y());
- if (start_line < 0)
- {
- start_line = 0;
- start_pos = 0;
- }
- else if (start_line >= text().size())
- {
- start_line = text().size()-1;
- start_pos = ((String)text().elementAt(start_line)).length();
- }
- else
- {
- start_pos = char_pos_of(start_line, evt.local_x());
- }
- set_selection(start_line, start_pos, start_line, start_pos);
-
- /* stash the star position in user_info (this is a bit of an abuse of a
- * point object, but...) */
- ((Point)user_info).x = start_line;
- ((Point)user_info).y = start_pos;
-
- return true;
-
- //later need to handle "extend" case (w/ shift down)
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept a movement during a drag.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean drag_feedback(event evt, Object user_info)
- {
- int line, pos;
- int start_line, start_pos;
-
- /* extract start loc from user_info */
- start_line = ((Point)user_info).x;
- start_pos = ((Point)user_info).y;
-
- /* set end of selection here (we rely on set_selection() to flip) */
- line = find_line_of(evt.local_y());
- if (line < 0)
- {
- line = 0;
- pos = 0;
- }
- else if (line >= text().size())
- {
- line = text().size()-1;
- pos = ((String)text().elementAt(line)).length();
- }
- else
- {
- pos = char_pos_of(line, evt.local_x());
- }
- set_selection(start_line, start_pos, line, pos);
-
-
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept input corresponding to the end of a drag.
- *
- * @param event evt the event to be dispatched.
- * @param Object user_info the object provided to the agent when this
- * interactor entered the focus set.
- * @return boolean true if this event was handled (which it always is in
- * this case).
- */
- public boolean drag_end(event evt, Object user_info)
- {
- /* let drag_feedback do the work */
- return drag_feedback(evt, user_info);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
- /*=========================== COPYRIGHT NOTICE ===========================
-
- This file is part of the subArctic user interface toolkit.
-
- Copyright (c) 1996 Scott Hudson and Ian Smith
- All rights reserved.
-
- The subArctic system is freely available for most uses under the terms
- and conditions described in
- http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html
- and appearing in full in the lib/interactor.java source file.
-
- The current release and additional information about this software can be
- found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
-
- ========================================================================*/
-